classdef ET_PURSUIT < ET_GEN

    properties

        FpCoccPE   = 330                % FpC = frames per cycle; 1 second @ 60Hz
        FpClearn   = [270,300,360,420]
        cycles     = 3                  % one cycle is there and back
        
        FpC

        % Fixation

        fix = struct('x',-0.5, ... % fraction of scn/2 size (for size of SPEM path)
            'y',0, ...
            'step',-0.5,...       % fixation cross and stimulus start to be step-ramp
            'time',2)           % ms

        % stimulus

        stim       = struct('size',15,'rgb',[1 1 1])

        % Things I dont understand
        
        % -----------------------------------------------------------------

        stim_speed_occPE
        stim_maxspeed_occPE

        stim_speed_learn
        stim_maxspeed_learn
        
        % -----------------------------------------------------------------
        view_dist  = 60    % distance from monitor in cm
        occluder
        jump
        refresh_rate  = 60
        resample      = 1000
        phase_jump    = 1/30
        pauses        = 32
        randomFixation
        refreshFreq   = 1
        stimuli       = inline('cos(x*2*pi)','x','f')
        
    end

    methods

        function self = ET_PURSUIT()
            
            self = self@ET_GEN();
            % New seed for random
            rand('twister',sum(100*clock));
            %self.el_pointer = EYELINK_EXP(self);
        end
        
        function prepareBlocks(self,nb)
            self.num_blocks = nb;        
        end

        function prepareTrials(self)
            % Creates matrix of trial types: 50% are '0' (will be occPE trials)
            % 12.5% each are '1,2,3,4' (will be different speeds in learning
            % trials)

            trialtype = zeros(self.num_trials,self.num_blocks);

            for b = 1:self.num_blocks
                trialtype(:,b) = randperm(self.num_trials);
                for t = 1:self.num_trials
                    if trialtype(t,b) > self.num_trials/2
                        trialtype(t,b) = 0;
                    end
                end
            end

            % creates matrix of target speeds, according to trial type
            % matrix above

            FpC = zeros(self.num_trials,self.num_blocks);
            for b = 1:self.num_blocks
                for t = 1:self.num_trials
                    if trialtype(b,t) == 0
                        FpC(t,b) = self.FpCoccPE;
                    else
                        FpC(t,b) = self.FpClearn(trialtype(t.b));
                    end
                end

            end
            % Save results

        end

        function prepareOccluder(self)
            % Saves an instance of occluder in the property occluder
            self.occluder = OCCLUDER(self);
        end
        
        function prepareFixation(self)
            
            %self.randomFixation = round(rand(self.num_blocks,self.num_trials));
            %self.randomFixation(self.randomFixation == 0) = -1;
            %self.randomFixation = self.randomFixation * self.fix.x;
            self.randomFixation = ones(self.num_blocks,self.num_trials) * self.fix.x;
        end

        function prepareSpeeds(self)

            % average & maximum stimulus speeds in degrees/sec

            for n = 1:numel(self.FpClearn)
                self.stim_speed_learn(n) = 2*atand((0.5 * (self.scn.wcm...
                    *-(self.fix.x))/self.view_dist)/((0.5*self.FpClearn(n))/60));
                self.stim_maxspeed_learn(n) = 60*atand(0.5*self.fix.x*cos(2*pi ...
                    *((0.25*self.FpClearn(n))+1)/self.FpClearn(n)) * ...
                    self.scn.wcm/self.view_dist);
            end

        end

        function prepareJump(self)

            % The property jump is filled with an instance of the class
            % jump
            self.jump = JUMP(self);

        end      
        

        function sendParameters(self,trial)
            [t,b] = ind2sub([self.num_blocks self.num_trials],trial);
            Eyelink('Message',['BLOCK ' num2str(b)]);
            Eyelink('Message',['TRIAL ' num2str(t)]);
            Eyelink('Message',['SUBJECT ' num2str(self.exp_par.sbj)]);
%             Eyelink('Message',['EXPERIMENT ' num2str(self.exp_par.exp)]);
            WaitSecs(0.3)
            Eyelink('Message',['FPC ' num2str(self.FpC(trial))]);
            Eyelink('Message',['OCC_X ' num2str(self.occluder.X(trial))]);
            Eyelink('Message',['OCC_W ' num2str(self.occluder.W(trial))]);
            Eyelink('Message',['JUMP_FRAME ' num2str(self.jump.frame(trial))]);
            WaitSecs(0.3)
            Eyelink('Message',['JUMP_dPHASE ' num2str(self.jump.m(trial))]);
            Eyelink('Message',['FIXATION ' sprintf('%0.2f',self.randomFixation(trial))]);
            % Saves a line with the function creating the stimuli
            %--------------------------------------------------------------
            Eyelink('Message',['STIMULI ' char(self.generateStimuli(trial))]);
        end
        
        function stimuli = generateStimuli(self,trial)
            % Generates a function using blocks and trials
            stimuli = self.stimuli;
        end
        
        function  v =  returnVectors(self,trial,resample)
            if nargin < 3; resample = self.resample; end
            
            
            % Tries to clear the internal states of stimuli
            %--------------------------------------------------------------
            self.clearFunction(self.stimuli);
            
            % Resample is the resample in Hz
            %--------------------------------------------------------------
            v  = nan(1,60000); %buffer
            i  = 1;
            c  = 1;
            dt = 0;
            
            % Time vector
            %--------------------------------------------------------------
            while dt <= self.cycles
                dt = dt+(self.refresh_rate/(resample*self.FpC(trial)));
                % In case it is close to the occludders center.
                %----------------------------------------------------------
                if c && abs(dt - self.jump.frame(trial)) < 2/self.FpC(trial)
                    dt = dt + self.jump.m(trial);
                    c = 0;
                end
                v(i) = dt;
                i    = i + 1;
            end
            
            % Signal vector
            %--------------------------------------------------------------
            stimuli = self.generateStimuli(trial);
            v = self.randomFixation(trial)*...
                (self.scn.w/2)*stimuli(v(~isnan(v)),self.FpC(trial))+ ...
                self.scn.w/2;
            
        end

        function hp = plotTrial(self,trial)

            v = self.plotOccluder(trial);
            
            hp = plot((0:numel(v)-1),v,'r','linewidth',1);
            set(gca,'xticklabel',get(gca,'xtick')/self.resample);
            
            % Plot occluder
            %--------------------------------------------------------------
            set(gca,'ylim',[0,self.scn.w]);
            xlabel('Secs')
            ylabel('Pixel')

        end

        function plotSpeed(self,trial)
            v = self.returnVectors(trial);
            plot((0:numel(v)-1)/self.resample,gradient(v),'k','linewidth',1)
            % Plot occluder

            title(['Instantaneous speed: trial ' int2str(trial)])
            xlabel('Secs')
            ylabel('Pixel/Sec')

        end
        
        function [points00,points01,points10,points11] = occludersPosition(self,trial)
           % Returns the position of the occluders in radians/pi 
            left  = (self.occluder.X(trial) - self.occluder.W(trial)/2)...
                / (self.randomFixation(trial)*(self.scn.w/2));
            right = (self.occluder.X(trial) + self.occluder.W(trial)/2)...
                / (self.randomFixation(trial)*(self.scn.w/2));
            if left < -1; left  = -1; end
            if right > 1; right = 1; end
            
            v0 = real(acos(left))/pi;
            v1 = real(acos(right))/pi;
            % The two solution of acos;
            points00 = ((0:self.cycles-1) * 2 + v0);
            points01 = ((0:self.cycles-1) * 2 + (2 - v0));
            points10 = ((0:self.cycles-1) * 2 + v1);
            points11 = ((0:self.cycles-1) * 2 + (2 - v1));
            % Adds the jump, if there is any.
            points00(points00 > self.jump.frame(trial)*2) = points00(points00 > self.jump.frame(trial)*2) - self.jump.m(trial)*2;
            points01(points01 > self.jump.frame(trial)*2) = points01(points01 > self.jump.frame(trial)*2) - self.jump.m(trial)*2;
            points10(points10 > self.jump.frame(trial)*2) = points10(points10 > self.jump.frame(trial)*2) - self.jump.m(trial)*2;
            points11(points11 > self.jump.frame(trial)*2) = points11(points11 > self.jump.frame(trial)*2) - self.jump.m(trial)*2;
           
        end

        function v = plotOccluder(self,trial,y0,y1,ah)
            % Plots the occluder

            if nargin < 5; ah = gca; end
            
            v = self.returnVectors(trial);
            
            
            if nargin < 3; y0 = 0; end
            if nargin < 4; y1 = self.scn.w; end
            
            oRight = self.scn.w/2 + self.occluder.X(trial) + self.occluder.W(trial)/2;
            oLeft  = self.scn.w/2 + self.occluder.X(trial) - self.occluder.W(trial)/2;
            
            ov =  (v < max(oRight,oLeft)).* (v > min(oRight,oLeft));

            %dps = (self.resample*self.FpC(trial))/(self.scn.refresh_rate*2);
            
            if ov(1) == 1; 
                points0 = 1; 
                points1 = 1; 
                t = 1;
            else
                points0 = [];
                points1 = [];
                t = 0;
            end
            for i = 2:numel(ov)
                if ov(i) && t
                    points1(end) = points1(end) + 1;
                elseif ov(i) && ~t
                    points0(end+1) = i;
                    points1(end+1) = i;
                    t = 1;
                elseif ~ov(i) && t
                    t = 0;
                end
            end
            patch([points0;points1;points1;points0],...
                kron(ones(1,numel(points0)),[y0,y0,y1,y1]'),[0.9,0.9,0.9],...
                'edgecolor',[0.9,0.9,0.9]); hold on
        end
        
        function saveLog(self,name)
           if nargin < 2; 
               name =['eyetrack.exp.' datestr(now,'mm_HH_DD_MM_YY') '.mat'];
           end
           self.date = now;
           save(name,'self')
        end

    end
    
    methods(Static)
       
        function status = clearFunction(fname)
            % Takes either an inline function or string and tries to clear
            % it.
            
            if isa(fname,'inline')
                fname = char(fname);
            end
            try
                %----------------------------------------------------------
                tmp = regexp(fname,'(.+)(\()','tokens');
                fprintf(1,'Reload %s\n',tmp{1}{1});
                clear(tmp{1}{1})
            catch
                status =0;
                return
            end
            status = 1;
            return

        end
        
    end
end